package com.gitee.feizns.v8;

import com.alibaba.fastjson.JSONObject;
import com.gitee.feizns.v8.json.ReadConfig;
import com.gitee.feizns.v8.rander.DataConfig;
import com.gitee.feizns.v8.rander.PackageConfig;
import com.gitee.feizns.v8.rander.Utils;
import com.gitee.feizns.v8.rander.table.Table;
import com.gitee.feizns.v8.utils.IOUtils;
import lombok.SneakyThrows;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.StringWriter;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.List;
import java.util.function.Function;

/**
 * @author feizns
 * @since 2019/8/14
 */
public class Generator {

    static ReadConfig config;

    static VelocityEngine engine = new VelocityEngine();

    static final File FILE;

    static {
        FILE = getJarDir().getParentFile();
        try {
            config = JSONObject.parseObject(IOUtils.readerToStr(new FileInputStream(new File(FILE, "meta.json"))), ReadConfig.class);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    static {
        engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
        engine.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
        engine.setProperty(Velocity.ENCODING_DEFAULT, "UTF-8");
        engine.setProperty(Velocity.INPUT_ENCODING, "UTF-8");
        engine.setProperty(Velocity.OUTPUT_ENCODING, "UTF-8");
        engine.init();
    }

    @SneakyThrows
    private static File getJarDir() {
        URL url = Generator.class.getResource("").toURI().toURL();
        String path = url.getFile();
        int i = path.lastIndexOf("!");
        if ( i != -1 )
            return new File(url.toString().substring(10, i));
        else
            return new File(url.toString().substring(10));
    }

    @SneakyThrows
    public static void main(String[] args) {
        //创建库创表
        createDbAndTable();

        //创建控制器
        generator("/src/main/java/", PackageConfig::getController, "/v8/Controller.vm", table -> table.getJavaClassName() + "Controller.java", null);
        generator("/src/main/java/", PackageConfig::getService, "/v8/Service.vm", table -> table.getJavaClassName() + "Service.java", null);
        generator("/src/main/java/", p -> p.getService() + ".impl", "/v8/ServiceImpl.vm", table -> table.getJavaClassName() + "ServiceImpl.java", null);
        generator("/src/main/java/", PackageConfig::getMapper, "/v8/Mapper.vm", table -> table.getJavaClassName() + "Mapper.java", null);
        generator("/src/main/resources/", PackageConfig::getMapper, "/v8/MapperXml.vm", table -> table.getJavaClassName() + "Mapper.xml", null);
        generator("/src/main/java/", PackageConfig::getPojo, "/v8/Pojo.vm", table -> table.getJavaClassName() + ".java", null);

        generator("/src/main/java/", PackageConfig::getAo, "/v8/ao/CreateAo.vm", table -> "CreateAo.java", t -> t.getTableNamePackage().replace(".", "/"));
        generator("/src/main/java/", PackageConfig::getAo, "/v8/ao/UpdateAo.vm", table -> "UpdateAo.java", t -> t.getTableNamePackage().replace(".", "/"));
        generator("/src/main/java/", PackageConfig::getAo, "/v8/ao/ListAo.vm", table -> table.getJavaClassName() + "ListAo.java", t -> t.getTableNamePackage().replace(".", "/"));
        generator("/src/main/java/", PackageConfig::getVo, "/v8/vo/ListVo.vm", table -> table.getJavaClassName() +"ListVo.java", t -> t.getTableNamePackage().replace(".", "/"));
        generator("/src/main/java/", PackageConfig::getVo, "/v8/vo/DetailsVo.vm", table -> table.getJavaClassName() + "DetailsVo.java", t -> t.getTableNamePackage().replace(".", "/"));

        generator("/src/main/java/", PackageConfig::getError, "/v8/error/ExistsCode.vm", table -> "ExistsCode.java", null);
        generator("/src/main/java/", PackageConfig::getError, "/v8/error/NotFoundCode.vm", table -> "NotFoundCode.java", null);
        generator("/src/main/java/", PackageConfig::getError, "/v8/error/Code.vm", table -> "Code.java", null);

        generator("/src/main/resources/", p -> "", "/v8/application.vm", table -> "application.properties", null);

        generator("/src/main/java/", PackageConfig::getConfig, "/v8/DataConfiguration.vm", table -> "DataAutoConfiguration.java", null);
        generator("/src/main/java/", PackageConfig::getConfig, "/v8/ErrorCodeConfiguration.vm", table -> "ErrorCodeConfiguration.java", null);

        generator("/src/main/java/", p -> p.getPrefix(), "/v8/Start.vm", table -> "Application.java", null);
        generator("/src/main/java/", p -> p.getPrefix(), "/v8/ServletInitializer.vm", table -> "ServletInitializer.java", null);

        generator("", p -> "", "/v8/gitignore.vm", table -> ".gitignore", null);

        generator("", p -> "", "/v8/pom.vm", table -> "pom.xml", null);

        System.out.println("生成成功.");
    }

    /**
     * 创建数据库和表
     */
    @SneakyThrows
    private static void createDbAndTable() {
        DataConfig dataConfig = config.getDataConfig();
        Class.forName("com.mysql.jdbc.Driver");
        Connection con = DriverManager.getConnection(dataConfig.getUrl(), dataConfig.getUsername(), dataConfig.getPassword());
        Statement statement = con.createStatement();
        statement.execute(String.format("CREATE DATABASE %s DEFAULT CHARACTER SET utf8 COLLATE utf8_bin", dataConfig.getDb()));
        statement.execute(String.format("USE %s", dataConfig.getDb()));
        List<Table> tables = config.getTables();
        for (Table table : tables) {
            String template = getTemplate("/v8/CreateTable.vm", table);
            statement.execute(template);
        }
        statement.close();
        con.close();
    }

    public static void generator(String base, Function<PackageConfig, String> pa, String templateLoc, Function<Table, String> filename, Function<Table, String> pckSuffix) {
        File dir = config.getProjectName() != null ? new File(FILE, config.getProjectName()) : new File(config.getDir());
        File baseDir = new File(dir, base + pa.apply(config.getPackageConfig()).replace(".", "/"));
        List<Table> tables = config.getTables();
        for (Table table : tables) {
            File newFile = baseDir;
            if ( pckSuffix != null )
                newFile =  new File(baseDir, pckSuffix.apply(table));

            write(new File(newFile, filename.apply(table)), getTemplate(templateLoc, table));
        }
    }

    public static String getTemplate(String location, Table table) {
        Template template = engine.getTemplate(location);
        VelocityContext vc = new VelocityContext();
        vc.put("utils", new Utils());
        vc.put("table", table);
        vc.put("tables", config.getTables());
        vc.put("author", config.getAuthor());
        vc.put("date", config.getCurrentDate());
        vc.put("packageConfig", config.getPackageConfig());
        vc.put("dataConfig", config.getDataConfig());
        vc.put("apiPrefix", config.getApiPrefix());
        StringWriter sw = new StringWriter();
        template.merge(vc, sw);
        return sw.toString();
    }

    public static void write(File file, String content) {
        file.getParentFile().mkdirs();
        IOUtils.write(content, file);
    }

}
